import external from "./../../externalModules.js"
import BaseAnnotationTool from "../base/BaseAnnotationTool.js"

// State
// import { getToolState } from './../../stateManagement/toolState.js';
import toolStyle from "./../../stateManagement/toolStyle.js"
import toolColors from "./../../stateManagement/toolColors.js"
import {
    removeToolState,
    getToolState
} from "./../../stateManagement/toolState.js"
//events
import EVENTS from "./../../events.js"
import triggerEvent from "./../../util/triggerEvent.js"

// Drawing
import {
    getNewContext,
    draw,
    drawHandles,
    drawRect,
    drawLinkedTextBox,
    setShadow
} from "./../../drawing/index.js"

// Util
import calculateSUV from "./../../util/calculateSUV.js"
import getROITextBoxCoords from "../../util/getROITextBoxCoords.js"
import numbersWithCommas from "./../../util/numbersWithCommas.js"
import throttle from "./../../util/throttle.js"
import { rectangleRoiCursor } from "../cursors/index.js"
import { getLogger } from "../../util/logger.js"
import getPixelSpacing from "../../util/getPixelSpacing"
import { getModule } from "../../store/index"

const logger = getLogger("tools:annotation:RectangleRoiTool")
import commonality from "@/utils/commonality"

/**
 * @public
 * @class RectangleRoiTool
 * @memberof Tools.Annotation
 * @classdesc Tool for drawing rectangular regions of interest, and measuring
 * the statistics of the enclosed pixels.
 * @extends Tools.Base.BaseAnnotationTool
 */
export default class RectangleRoiTool extends BaseAnnotationTool {
    constructor(props = {}) {
        const defaultProps = {
            name: "RectangleRoi",
            supportedInteractionTypes: ["Mouse", "Touch"],
            configuration: {
                changeTextCallback,
                drawHandles: true,
                drawHandlesOnHover: false,
                hideHandlesIfMoving: false,
                renderDashed: false
                // showMinMax: false,
                // showHounsfieldUnits: true
            },
            svgCursor: rectangleRoiCursor
        }

        super(props, defaultProps)

        this.throttledUpdateCachedStats = throttle(this.updateCachedStats, 110)
    }
    //双击设置圆形工具的roi属性
    doubleClickCallback(evt) {
        // console.log("circleroi doubelclick")
        return this._updateTextForNearbyAnnotation(evt)
    }

    _updateTextForNearbyAnnotation(evt) {
        // console.log("circleroi evt",evt);
        // console.log("_updateTextForNearbyAnnotation")
        const element = evt.detail.element
        const coords = evt.detail.currentPoints.canvas
        const toolState = getToolState(element, this.name)
        //这里绑定的是鼠标双击事件 所以用的是眸色
        const interactionType = "mouse"

        if (!toolState) {
            return false
        }
        // console.log("circleroi toolState");

        for (let i = 0; i < toolState.data.length; i++) {
            const data = toolState.data[i]
            // console.log("datatool ArrowAnnotatateTool", data)
            if (this.pointNearTool(element, data, coords, interactionType)) {
                data.active = true
                external.cornerstone.updateImage(element)
                // Allow relabelling via a callback
                this.configuration.changeTextCallback(
                    data,
                    evt.detail,
                    this._doneChangingTextCallback.bind(this, element, data)
                )
            }
        }
        evt.stopImmediatePropagation()
        evt.preventDefault()
        evt.stopPropagation()
        return true
    }
    //处理添加的标签的函数
    _doneChangingTextCallback(
        element,
        measurementData,
        updatedText,
        deleteTool
    ) {
        // debugger;
        // 修改文字标签和颜色
        console.log(commonality)
        if (deleteTool === true) {
            removeToolState(element, this.name, measurementData)
        } else {
            let text = commonality.labelText
            let color = commonality.labelColor
            measurementData.text = text
            measurementData.color = color
        }
        // console.log("_doneChangingTextCallback in circleroi", measurementData)
        measurementData.active = false
        external.cornerstone.updateImage(element)

        triggerEvent(element, EVENTS.MEASUREMENT_MODIFIED, {
            toolName: this.name,
            toolType: this.name, // Deprecation notice: toolType will be replaced by toolName
            element,
            measurementData
        })
    }

    createNewMeasurement(eventData) {
        // console.log(eventData)
        const goodEventData =
            eventData &&
            eventData.currentPoints &&
            eventData.currentPoints.image

        // if (!goodEventData) {
        //     logger.error(
        //         `required eventData not supplied to tool ${this.name}'s createNewMeasurement`
        //     )

        //     return
        // }

        return {
            visible: true,
            active: true,
            color: "",
            invalidated: true,
            handles: {
                start: {
                    x: eventData.currentPoints.image.x,
                    y: eventData.currentPoints.image.y,
                    highlight: true,
                    active: false
                },
                end: {
                    x: eventData.currentPoints.image.x,
                    y: eventData.currentPoints.image.y,
                    highlight: true,
                    active: true
                },
                initialRotation: eventData.viewport.rotation,
                // 文本框
                textBox: {
                    active: false,
                    // 是否可以移动
                    hasMoved: false,
                    // 是否立刻移动
                    movesIndependently: false,
                    // 是否立刻绘制
                    drawnIndependently: true,
                    // 是否允许外部图像
                    allowedOutsideImage: true,
                    hasBoundingBox: true
                }
            }
        }
    }

    pointNearTool(element, data, coords, interactionType) {
        // console.log(1)
        const hasStartAndEndHandles =
            data && data.handles && data.handles.start && data.handles.end
        const validParameters = hasStartAndEndHandles

        // if (!validParameters) {
        //     logger.warn(
        //         `invalid parameters supplied to tool ${this.name}'s pointNearTool`
        //     )
        // }

        if (!validParameters || data.visible === false) {
            return false
        }

        const distance = interactionType === "mouse" ? 15 : 25
        const startCanvas = external.cornerstone.pixelToCanvas(
            element,
            data.handles.start
        )
        const endCanvas = external.cornerstone.pixelToCanvas(
            element,
            data.handles.end
        )

        const rect = {
            left: Math.min(startCanvas.x, endCanvas.x),
            top: Math.min(startCanvas.y, endCanvas.y),
            width: Math.abs(startCanvas.x - endCanvas.x),
            height: Math.abs(startCanvas.y - endCanvas.y)
        }

        const distanceToPoint = external.cornerstoneMath.rect.distanceToPoint(
            rect,
            coords
        )

        return distanceToPoint < distance
    }

    updateCachedStats(image, element, data) {
        const seriesModule =
            external.cornerstone.metaData.get(
                "generalSeriesModule",
                image.imageId
            ) || {}
        const modality = seriesModule.modality
        const pixelSpacing = getPixelSpacing(image)
        const stats = _calculateStats(
        image,
        element,
        data.handles,
        modality,
        pixelSpacing
        )

        data.cachedStats = stats
        data.invalidated = false
    }

    /**
     * 绘制图像内容
     * @author hsl
     * @date 2021-08-04
     * @param {any} evt
     * @returns {any}
     */
    renderToolData(evt) {
        // console.log(3)
        const toolData = getToolState(evt.currentTarget, this.name)

        if (!toolData) {
            return
        }

        const eventData = evt.detail
        const { image, element } = eventData
        const lineWidth = toolStyle.getToolWidth()
        const lineDash = getModule("globalConfiguration").configuration.lineDash
        const {
            handleRadius,
            drawHandlesOnHover,
            hideHandlesIfMoving,
            renderDashed
        } = this.configuration
        const context = getNewContext(eventData.canvasContext.canvas)
        const { rowPixelSpacing, colPixelSpacing } = getPixelSpacing(image)

        // Meta
        const seriesModule =
            external.cornerstone.metaData.get(
                "generalSeriesModule",
                image.imageId
            ) || {}

        // Pixel Spacing
        const modality = seriesModule.modality
        const hasPixelSpacing = rowPixelSpacing && colPixelSpacing

        draw(context, context => {
            // If we have tool data for this element - iterate over each set and draw it
            for (let i = 0; i < toolData.data.length; i++) {
                const data = toolData.data[i]

                if (data.visible === false) {
                    continue
                }

                // Configure
                const color = toolColors.getColorIfActive(data)
                const handleOptions = {
                    color,
                    handleRadius,
                    drawHandlesIfActive: drawHandlesOnHover,
                    hideHandlesIfMoving
                }

                setShadow(context, this.configuration)

                const rectOptions = { color }

                if (renderDashed) {
                    rectOptions.lineDash = lineDash
                }

                // Draw
                drawRect(
                    context,
                    element,
                    data.handles.start,
                    data.handles.end,
                    rectOptions,
                    "pixel",
                    data.handles.initialRotation
                )

                if (this.configuration.drawHandles) {
                    drawHandles(context, eventData, data.handles, handleOptions)
                }

                // Update textbox stats
                if (data.invalidated === true) {
                    if (data.cachedStats) {
                        this.throttledUpdateCachedStats(image, element, data)
                    } else {
                        this.updateCachedStats(image, element, data)
                    }
                }

                // Default to textbox on right side of ROI
                if (!data.handles.textBox.hasMoved) {
                    const defaultCoords = getROITextBoxCoords(
                        eventData.viewport,
                        data.handles
                    )

                    Object.assign(data.handles.textBox, defaultCoords)
                }

                const textBoxAnchorPoints = handles =>
                    _findTextBoxAnchorPoints(handles.start, handles.end)
                    console.log(data)
                const textBoxContent = _createTextBoxContent(
                    context,
                    image.color,
                    data.cachedStats,
                    modality,
                    hasPixelSpacing,
                    this.configuration
                )
                // const textBoxContent = []
                //此处添加属性标签到文字显示
                if (data.text != undefined) {
                    textBoxContent.push(data.text)
                } else {
                    textBoxContent.push("")
                }
                data.unit = ""
                // data.unit = _getUnit(
                //     modality,
                //     this.configuration.showHounsfieldUnits
                // )

                drawLinkedTextBox(
                    context,
                    element,
                    data.handles.textBox,
                    textBoxContent,
                    data.handles,
                    textBoxAnchorPoints,
                    color,
                    lineWidth,
                    10,
                    true
                )
            }
        })
    }
}

/**
 * TODO: This is the same method (+ GetPixels) for the other ROIs
 * TODO: The pixel filtering is the unique bit
 *
 * @param {*} startHandle
 * @param {*} endHandle
 * @returns {{ left: number, top: number, width: number, height: number}}
 */
function _getRectangleImageCoordinates(startHandle, endHandle) {
    return {
        left: Math.min(startHandle.x, endHandle.x),
        top: Math.min(startHandle.y, endHandle.y),
        width: Math.abs(startHandle.x - endHandle.x),
        height: Math.abs(startHandle.y - endHandle.y)
    }
}

/**
 *
 *
 * @param {*} image
 * @param {*} element
 * @param {*} handles
 * @param {*} modality
 * @param {*} pixelSpacing
 * @returns {Object} The Stats object
 */
function _calculateStats(image, element, handles, modality, pixelSpacing) {
    // Retrieve the bounds of the rectangle in image coordinates
    const roiCoordinates = _getRectangleImageCoordinates(
        handles.start,
        handles.end
    )

    // Retrieve the array of pixels that the rectangle bounds cover
    const pixels = external.cornerstone.getPixels(
        element,
        roiCoordinates.left,
        roiCoordinates.top,
        roiCoordinates.width,
        roiCoordinates.height
    )

    // Calculate the mean & standard deviation from the pixels and the rectangle details
    const roiMeanStdDev = _calculateRectangleStats(pixels, roiCoordinates)

    let meanStdDevSUV

    if (modality === "PT") {
        meanStdDevSUV = {
            mean: calculateSUV(image, roiMeanStdDev.mean, true) || 0,
            stdDev: calculateSUV(image, roiMeanStdDev.stdDev, true) || 0
        }
    }

    // Calculate the image area from the rectangle dimensions and pixel spacing
    const area =
        roiCoordinates.width *
        (pixelSpacing.colPixelSpacing || 1) *
        (roiCoordinates.height * (pixelSpacing.rowPixelSpacing || 1))

    // const area = "12345"

    return {
        area: area || 0,
        count: roiMeanStdDev.count || 0,
        mean: roiMeanStdDev.mean || 0,
        variance: roiMeanStdDev.variance || 0,
        stdDev: roiMeanStdDev.stdDev || 0,
        min: roiMeanStdDev.min || 0,
        max: roiMeanStdDev.max || 0,
        meanStdDevSUV
    }
}

/**
 *
 *
 * @param {*} sp
 * @param {*} rectangle
 * @returns {{ count, number, mean: number,  variance: number,  stdDev: number,  min: number,  max: number }}
 */
function _calculateRectangleStats(sp, rectangle) {
    let sum = 0
    let sumSquared = 0
    let count = 0
    let index = 0
    let min = sp ? sp[0] : null
    let max = sp ? sp[0] : null

    for (let y = rectangle.top; y < rectangle.top + rectangle.height; y++) {
        for (
            let x = rectangle.left;
            x < rectangle.left + rectangle.width;
            x++
        ) {
            sum += sp[index]
            sumSquared += sp[index] * sp[index]
            min = Math.min(min, sp[index])
            max = Math.max(max, sp[index])
            count++ // TODO: Wouldn't this just be sp.length?
            index++
        }
    }

    if (count === 0) {
        return {
            count,
            mean: 0.0,
            variance: 0.0,
            stdDev: 0.0,
            min: 0.0,
            max: 0.0
        }
    }

    const mean = sum / count
    const variance = sumSquared / count - mean * mean

    return {
        count,
        mean,
        variance,
        stdDev: Math.sqrt(variance),
        min,
        max
    }
}

/**
 *
 *
 * @param {*} startHandle
 * @param {*} endHandle
 * @returns {Array.<{x: number, y: number}>}
 */
function _findTextBoxAnchorPoints(startHandle, endHandle) {
    const { left, top, width, height } = _getRectangleImageCoordinates(
        startHandle,
        endHandle
    )

    return [
        {
            // Top middle point of rectangle
            x: left + width / 2,
            y: top
        },
        {
            // Left middle point of rectangle
            x: left,
            y: top + height / 2
        },
        {
            // Bottom middle point of rectangle
            x: left + width / 2,
            y: top + height
        },
        {
            // Right middle point of rectangle
            x: left + width,
            y: top + height / 2
        }
    ]
}

/**
 *
 *
 * @param {*} area
 * @param {*} hasPixelSpacing
 * @returns {string} The formatted label for showing area
 */
function _formatArea(area, hasPixelSpacing) {
    // This uses Char code 178 for a superscript 2
    const suffix = hasPixelSpacing
        ? ` mm${String.fromCharCode(178)}`
        : ` px${String.fromCharCode(178)}`

    return `Area: ${numbersWithCommas(area.toFixed(2))}${suffix}`
}

function _getUnit(modality, showHounsfieldUnits) {
    return modality === "CT" && showHounsfieldUnits !== false ? "HU" : ""
}

/**
 * TODO: This is identical to EllipticalROI's same fn
 * TODO: We may want to make this a utility for ROIs with these values?
 *
 * @param {*} context
 * @param {*} isColorImage
 * @param {*} { area, mean, stdDev, min, max, meanStdDevSUV }
 * @param {*} modality
 * @param {*} hasPixelSpacing
 * @param {*} [options={}]
 * @returns {string[]}  方法已注释
 */
function _createTextBoxContent(
    context,
    isColorImage,
    { area, mean, stdDev, min, max, meanStdDevSUV },
    modality,
    hasPixelSpacing,
    options = {}
) {
    // console.log(mean)
    // console.log(stdDev)
    // console.log(min)
    // console.log(max)
    // console.log(meanStdDevSUV)
    // console.log("计算属性")
    const showMinMax = options.showMinMax || false
    const textLines = []

    const otherLines = []

    if (!isColorImage) {
        const hasStandardUptakeValues =
            meanStdDevSUV && meanStdDevSUV.mean !== 0
        const unit = _getUnit(modality, options.showHounsfieldUnits)

        let meanString = `Mean: ${numbersWithCommas(mean.toFixed(2))} ${unit}`
        const stdDevString = `Std Dev: ${numbersWithCommas(
            stdDev.toFixed(2)
        )} ${unit}`

        // If this image has SUV values to display, concatenate them to the text line
        if (hasStandardUptakeValues) {
            const SUVtext = " SUV: "

            const meanSuvString = `${SUVtext}${numbersWithCommas(
                meanStdDevSUV.mean.toFixed(2)
            )}`
            const stdDevSuvString = `${SUVtext}${numbersWithCommas(
                meanStdDevSUV.stdDev.toFixed(2)
            )}`

            const targetStringLength = Math.floor(
                context.measureText(`${stdDevString}     `).width
            )

            while (context.measureText(meanString).width < targetStringLength) {
                meanString += " "
            }

            otherLines.push(`${meanString}${meanSuvString}`)
            otherLines.push(`${stdDevString}     ${stdDevSuvString}`)
        } else {
            otherLines.push(`${meanString}`)
            otherLines.push(`${stdDevString}`)
        }

        if (showMinMax) {
            let minString = `Min: ${min} ${unit}`
            const maxString = `Max: ${max} ${unit}`
            const targetStringLength = hasStandardUptakeValues
                ? Math.floor(context.measureText(`${stdDevString}     `).width)
                : Math.floor(context.measureText(`${meanString}     `).width)

            while (context.measureText(minString).width < targetStringLength) {
                minString += " "
            }

            otherLines.push(`${minString}${maxString}`)
        }
        console.log(otherLines)
    }

    textLines.push(_formatArea(area, hasPixelSpacing))
    otherLines.forEach(x => textLines.push(x))

    return textLines
}
//修改标签属性的回调函数(双击)
function changeTextCallback(data, eventData, doneChangingTextCallback) {
    // prompt("Change your annotation:")
    doneChangingTextCallback("矩形")
}
